package miage.rest.xml.xstream.converter;

import miage.rest.xml.xstream.converter.annotations.HrefVariable;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.ConverterLookup;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import org.restlet.routing.Template;

/**
 * Cette classe est un converter pour XStream
 * Il s'agit plus précisément d'un décorateur de converter.
 * Son but est de serializer / deserializer la classe cible "targetClass" afin
 * d'y rajouter les attributs de lien "href". Ces liens href sont créees à partir
 * du template d'URI passé en paramètre.
 *
 * Comme il s'agit d'un décorateur, le converter à décorer est à passé en paramètre
 * Si le converter à décorer ne change pas par rapport a celui qui serait normalement
 * utilisé pour la classe targetClass il suffit de fournir une instance de converterLookup,
 * ainsi la converter par défaut sera directement décoré.
 *
 *
 *
 * @author Schwartz Michaël
 */
public class HrefConverter extends AbstractConverterDecorator {
	private final Template uriTemplate;
	private final Class targetClass;

	/**
	 * @require uriTemplate != null
	 * @require target != null
	 *
	 * @param target
	 * @param decoratedConverter
	 * @param uriTemplate
	 */
	public HrefConverter(Class target, Converter decoratedConverter, Template uriTemplate) {
		super(decoratedConverter);
		this.targetClass = target;
		this.uriTemplate = uriTemplate;
	}

	/**
	 * @require uriTemplate != null
	 * @require target != null
	 * 
	 * @param target
	 * @param converterLookup
	 * @param uriTemplate
	 */
	public HrefConverter(Class target, ConverterLookup converterLookup, Template uriTemplate) {
		super(target, converterLookup);
		this.targetClass = target;
		this.uriTemplate = uriTemplate;
	}
	
	@Override
	public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
		if (uriTemplate != null) {
			Map<String, ?> values = this.retrieveHrefValues(source);
			writer.addAttribute("href", uriTemplate.format(values));
		}
		this.getDecoratedConverter().marshal(source, writer, context);
	}

	/**
	 * @todo Méthode à améliorer pour que la conversion soit possible si et seulement si
	 * le template d'URI passé en paramètre peut être complètement satisfait par
	 * les annotations de la classe "type"
	 * 
	 * @param type
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public boolean canConvert(Class type) {
		boolean result = false;
		
		if (type.isAssignableFrom(targetClass)) {
			for(Field field : type.getDeclaredFields()) {
				if (field.isAnnotationPresent(HrefVariable.class)) {
					result = true;
					break;
				}
			}
		}
		return result;
	}


	protected Map<String, ?> retrieveHrefValues(Object source) {
		Class type = source.getClass();
		String hrefVariableName;
		Object hrefVariableValue;
		Map<String, Object> hrefValues = new HashMap<String, Object>();
		
		for(Field field : type.getDeclaredFields()) {
			if (field.isAnnotationPresent(HrefVariable.class)) {
				try {
					field.setAccessible(true);
					
					hrefVariableName = field.getName();
					hrefVariableValue = field.get(source);

					hrefValues.put(hrefVariableName, hrefVariableValue);
				} catch (Exception ex) {
					//Ignore this field if an exception is caught
				}
			}
		}

		return hrefValues;
	}
}
